package handlers.aioitemhandler;

import java.util.logging.Logger;

import com.l2jserver.gameserver.handler.IAIOItemHandler;

import java.util.Iterator;
import java.util.Set;

import com.l2jserver.Config;
import com.l2jserver.gameserver.cache.HtmCache;
import com.l2jserver.gameserver.datatables.CharTemplateTable;

import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.base.PlayerClass;
import com.l2jserver.gameserver.model.base.Race;
import com.l2jserver.gameserver.model.base.SubClass;

import com.l2jserver.gameserver.model.quest.QuestState;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.util.StringUtil;

/**
 * @implemented from L2VillageMasterInstance.java , by Phear3d
 */
public class AIOSubClassHandler implements IAIOItemHandler 
{
	private static final Logger _log = Logger.getLogger(AIOSubClassHandler.class.getName());
	private static final String BYPASS = "Subclass";
	private static final String ROOT = "data/html/aioitem/";

	@Override
	public String getBypass() 
	{
		return BYPASS;
	}

	@Override

	public void onBypassUse(L2PcInstance player, String command)
	{
	  NpcHtmlMessage html = new NpcHtmlMessage(5);
	  NpcHtmlMessage msg = new NpcHtmlMessage(5);
		
	  if (command.startsWith("Subclass"))
	  {
		if (!Config.ALT_GAME_SUBCLASS_EVERYWHERE)
		{
			final String fileRoot = ROOT + "SubClassDisabled.htm";
			final String htm = HtmCache.getInstance().getHtm(null, fileRoot);
			if(htm != null)
			{
				msg.setHtml(htm);
				player.sendPacket(msg);
				return;
			}
		}
		// Subclasses may not be changed while a skill is in use.
		if (player.isCastingNow() || player.isAllSkillsDisabled())
		{
			player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUBCLASS_NO_CHANGE_OR_CREATE_WHILE_SKILL_IN_USE));
			return;
		}
				
		if (player.getTransformation() != null)
		{
			final String fileRoot = ROOT + "SubClassNoTransformed.htm";
			final String htm = HtmCache.getInstance().getHtm(null, fileRoot);
			if(htm != null)
			{
				msg.setHtml(htm);
				player.sendPacket(msg);
				return;
			}
		}
		
		int cmdChoice = 0;
		int paramOne = 0;
		//int paramTwo = 0;
		
		try
		{
			cmdChoice = Integer.parseInt(command.substring(9, 10).trim());
			
			int endIndex = command.indexOf(' ', 11);
			if (endIndex == -1)
				endIndex = command.length();
			
			paramOne = Integer.parseInt(command.substring(11, endIndex).trim());
			//if (command.length() > endIndex)
				//paramTwo = Integer.parseInt(command.substring(endIndex).trim());
		}
		catch (Exception NumberFormatException)
		{
		}
		
		switch (cmdChoice)
		{
			case 0: // Subclass change menu
				html.setFile(null, getSubClassMenu(player.getRace()));
				break;
			case 1: // Add Subclass - Initial
				// Avoid giving player an option to add a new sub class, if they have three already.
				if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS)
				{
					html.setFile(null, getSubClassFail());
					break;
				}
				
				html.setFile(null, ROOT + "SubClassAdd.htm");
				final StringBuilder content1 = StringUtil.startAppend(200);
				Set<PlayerClass> subsAvailable = getAvailableSubClasses(player);
				
				if (subsAvailable != null && !subsAvailable.isEmpty())
				{
					for (PlayerClass subClass : subsAvailable)
					{
						StringUtil.append(content1,
								"<a action=\"bypass -h Aioitem_Subclass_Subclass 4 ",
								String.valueOf(subClass.ordinal()),
								"\" msg=\"1268;",
								formatClassForDisplay(subClass),
								"\">",
								formatClassForDisplay(subClass),
						"</a><br>");
					}
				}
				else
				{
					// TODO: Retail message
					player.sendMessage("There are no sub classes available at this time.");
					return;
				}
				html.replace("%list%", content1.toString());
				break;
			case 2: // Change Class - Initial
				if (player.getSubClasses().isEmpty())
					html.setFile(null, ROOT + "SubClassChangeNo.htm");
				else
				{
					final StringBuilder content2 = StringUtil.startAppend(200);
					
					if (checkVillageMaster(player.getBaseClass()))
					{
						StringUtil.append(content2,
								"<a action=\"bypass -h Aioitem_Subclass_Subclass 5 0\">",
								CharTemplateTable.getInstance().getClassNameById(player.getBaseClass()),
						"</a><br>");
					}
					
					for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
					{
						SubClass subClass = subList.next();
						if (checkVillageMaster(subClass.getClassDefinition()))
						{
							StringUtil.append(content2,
									"<a action=\"bypass -h Aioitem_Subclass_Subclass 5 ",
									String.valueOf(subClass.getClassIndex()),
									"\">",
									formatClassForDisplay(subClass.getClassDefinition()),
							"</a><br>");
						}
					}
					
					if (content2.length() > 0)
					{
						html.setFile(null, ROOT + "SubClassChange.htm");
						html.replace("%list%", content2.toString());
					}
					else
						html.setFile(null, ROOT + "SubClassChangeNotFound.htm");
				}
				break;
			case 4: // Add Subclass - Action (Subclass 4 x[x])
				/*
				 * If the character is less than level 75 on any of their previously chosen
				 * classes then disallow them to change to their most recently added sub-class choice.
				 */
				
				if (!player.getFloodProtectors().getSubclass().tryPerformAction("add subclass"))
				{
					_log.warning("Player "+player.getName()+" has performed a subclass change too fast");
					return;
				}
				
				boolean allowAddition = true;
				
				if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS)
					allowAddition = false;
				
				if (player.getLevel() < 75)
					allowAddition = false;
				
				if (allowAddition)
				{
					if (!player.getSubClasses().isEmpty())
					{
						for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
						{
							SubClass subClass = subList.next();
							
							if (subClass.getLevel() < 75)
							{
								allowAddition = false;
								break;
							}
						}
					}
				}
				
				/*
				 * If quest checking is enabled, verify if the character has completed the Mimir's Elixir (Path to Subclass)
				 * and Fate's Whisper (A Grade Weapon) quests by checking for instances of their unique reward items.
				 *
				 * If they both exist, remove both unique items and continue with adding the sub-class.
				 */
				if (allowAddition && !Config.ALT_GAME_SUBCLASS_WITHOUT_QUESTS)
					allowAddition = checkQuests(player);
				
				if (allowAddition && isValidNewSubClass(player, paramOne))
				{
					if (!player.addSubClass(paramOne, player.getTotalSubClasses() + 1))
						return;
					
					player.setActiveClass(player.getTotalSubClasses());
					
					html.setFile(null, ROOT + "SubClassAddOk.htm");
					
					player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ADD_NEW_SUBCLASS)); // Subclass added.
				}
				else
					html.setFile(null, getSubClassFail());
				break;
			case 5: // Change Class - Action
				/*
				 * If the character is less than level 75 on any of their previously chosen
				 * classes then disallow them to change to their most recently added sub-class choice.
				 *
				 * Note: paramOne = classIndex
				 */
				
				if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class"))
				{
					_log.warning("Player "+player.getName()+" has performed a subclass change too fast");
					return;
				}
				
				if (player.getClassIndex() == paramOne)
				{
					html.setFile(null, ROOT + "SubClassCurrent.htm");
					break;
				}
				
				if (paramOne == 0)
				{
					if (!checkVillageMaster(player.getBaseClass()))
						return;
				}
				else
				{
					try
					{
						if (!checkVillageMaster(player.getSubClasses().get(paramOne).getClassDefinition()))
							return;
					}
					catch (NullPointerException e)
					{
						return;
					}
				}
				
				player.setActiveClass(paramOne);
				
				player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUBCLASS_TRANSFER_COMPLETED)); // Transfer completed.
				return;
		}
		
		player.sendPacket(html);
	  }
	  else
	  {
			_log.severe("Wrong bypass for the AIOItem SubClass tag: "+command);
			return;
	  }
	}

	
	protected String getSubClassMenu(Race pRace)
	{
		if (Config.ALT_GAME_SUBCLASS_EVERYWHERE
				|| pRace != Race.Kamael)
			return ROOT + "SubClassMain.htm";
		
		return ROOT + "SubClassNoOther.htm";
	}
	
	protected String getSubClassFail()
	{
		return ROOT + "SubClassFail.htm";
	}
	
	protected boolean checkQuests(L2PcInstance player)
	{
		// Noble players can add subbclasses without quests
		if (player.isNoble())
			return true;
		
		QuestState qs = player.getQuestState("234_FatesWhisper");
		if (qs == null || !qs.isCompleted())
			return false;
		
		qs = player.getQuestState("235_MimirsElixir");
		if (qs == null || !qs.isCompleted())
			return false;
		
		return true;
	}
	
	/*
	 * Returns list of available subclasses
	 * Base class and already used subclasses removed
	 */
	private final Set<PlayerClass> getAvailableSubClasses(L2PcInstance player)
	{
		// get player base class
		final int currentBaseId = player.getBaseClass();
		final ClassId baseCID = ClassId.values()[currentBaseId];
		
		// we need 2nd occupation ID
		final int baseClassId;
		if (baseCID.level() > 2)
			baseClassId = baseCID.getParent().ordinal();
		else
			baseClassId = currentBaseId;
		
		/**
		 * If the race of your main class is Elf or Dark Elf,
		 * you may not select each class as a subclass to the other class.
		 *
		 * If the race of your main class is Kamael, you may not subclass any other race
		 * If the race of your main class is NOT Kamael, you may not subclass any Kamael class
		 *
		 * You may not select Overlord and Warsmith class as a subclass.
		 *
		 * You may not select a similar class as the subclass.
		 * The occupations classified as similar classes are as follows:
		 *
		 * Treasure Hunter, Plainswalker and Abyss Walker
		 * Hawkeye, Silver Ranger and Phantom Ranger
		 * Paladin, Dark Avenger, Temple Knight and Shillien Knight
		 * Warlocks, Elemental Summoner and Phantom Summoner
		 * Elder and Shillien Elder
		 * Swordsinger and Bladedancer
		 * Sorcerer, Spellsinger and Spellhowler
		 *
		 * Also, Kamael have a special, hidden 4 subclass, the inspector, which can
		 * only be taken if you have already completed the other two Kamael subclasses
		 *
		 */
		Set<PlayerClass> availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player);
		
		if (availSubs != null && !availSubs.isEmpty())
		{
			for (Iterator<PlayerClass> availSub = availSubs.iterator(); availSub.hasNext();)
			{
				PlayerClass pclass = availSub.next();
				
				// check for the village master
				if (!checkVillageMaster(pclass))
				{
					availSub.remove();
					continue;
				}
				
				// scan for already used subclasses
				int availClassId = pclass.ordinal();
				ClassId cid = ClassId.values()[availClassId];
				for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
				{
					SubClass prevSubClass = subList.next();
					ClassId subClassId = ClassId.values()[prevSubClass.getClassId()];
					
					if (subClassId.equalsOrChildOf(cid))
					{
						availSub.remove();
						break;
					}
				}
			}
		}
		
		return availSubs;
	}
	
	/*
	 * Check new subclass classId for validity
	 * (villagemaster race/type, is not contains in previous subclasses,
	 * is contains in allowed subclasses)
	 * Base class not added into allowed subclasses.
	 */
	private final boolean isValidNewSubClass(L2PcInstance player, int classId)
	{
		if (!checkVillageMaster(classId))
			return false;
		
		final ClassId cid = ClassId.values()[classId];
		for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
		{
			SubClass sub = subList.next();
			ClassId subClassId = ClassId.values()[sub.getClassId()];
			
			if (subClassId.equalsOrChildOf(cid))
				return false;
		}
		
		// get player base class
		final int currentBaseId = player.getBaseClass();
		final ClassId baseCID = ClassId.values()[currentBaseId];
		
		// we need 2nd occupation ID
		final int baseClassId;
		if (baseCID.level() > 2)
			baseClassId = baseCID.getParent().ordinal();
		else
			baseClassId = currentBaseId;
		
		Set<PlayerClass> availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player);
		if (availSubs == null || availSubs.isEmpty())
			return false;
		
		boolean found = false;
		for (Iterator<PlayerClass> availSub = availSubs.iterator(); availSub.hasNext();)
		{
			PlayerClass pclass = availSub.next();
			if (pclass.ordinal() == classId)
			{
				found = true;
				break;
			}
		}
		
		return found;
	}
	
	protected boolean checkVillageMasterRace(PlayerClass pclass)
	{
		return true;
	}
	
	protected boolean checkVillageMasterTeachType(PlayerClass pclass)
	{
		return true;
	}
	
	/*
	 * Returns true if this classId allowed for master
	 */
	public final boolean checkVillageMaster(int classId)
	{
		return checkVillageMaster(PlayerClass.values()[classId]);
	}
	
	/*
	 * Returns true if this PlayerClass is allowed for master
	 */
	public final boolean checkVillageMaster(PlayerClass pclass)
	{
		if (Config.ALT_GAME_SUBCLASS_EVERYWHERE)
			return true;
		
		return checkVillageMasterRace(pclass) && checkVillageMasterTeachType(pclass);
	}
	
	private static final String formatClassForDisplay(PlayerClass className)
	{
		String classNameStr = className.toString();
		char[] charArray = classNameStr.toCharArray();
		
		for (int i = 1; i < charArray.length; i++)
		{
			if (Character.isUpperCase(charArray[i]))
				classNameStr = classNameStr.substring(0, i) + " " + classNameStr.substring(i);
		}
		
		return classNameStr;
	}
	
	private static final Iterator<SubClass> iterSubClasses(L2PcInstance player)
	{
		return player.getSubClasses().values().iterator();
	}
}